const fs = require('fs'),
  path = require('path'),
  cluster = require('cluster'),
  isMaster = cluster.isMaster,
  workers = {},
  _ = require('lodash'),
  rootPath = path.dirname(__dirname);
let daemonize = false,
  config = {},
  log4js = require('log4js');
let pwdPath = process.cwd();
const BufferReader = require('./bufferreader');
let core = {
  getRootPath: function () {
    return rootPath;
  },
  getPath: function (name) {
    return path.resolve(rootPath, name);
  },
  getRealPath: function (name) {
    return path.resolve(pwdPath, name);
  },
  getConfig: function (section) {
    if (section) {
      // 查看配置文件是否存在，存在则加载进来
      if (!config[section] && fs.existsSync(`${pwdPath}/etc/${section}.js`)) {
        config[section] = require(`${pwdPath}/etc/${section}.js`);
      }
      return config[section] || {};
    }
    return config;
  },
  randomString: function (len, type) {
    var randstring = '1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ',
      result = [],
      length, i;
    if (type !== undefined) {
      if (type === 10 || type === 16) {
        randstring.substr(0, type);
      } else if (type === 'a') {
        randstring.substr(10);
      }
    }
    length = randstring.length - 1;
    for (i = 0; i < len; ++i) {
      result.push(randstring.substr(Math.floor(Math.random() * length), 1));
    }
    return result.join('');
  },
  BufferReader: BufferReader,
  concat: (bufs) => Buffer.concat(bufs, _.reduce(bufs, (length, buf) => length + buf.length, 0)),
  createIntBuffer: (value, length) => {
    let buf = Buffer.alloc(length);
    if (length == 1) {
      buf.writeUInt8(value);
    } else if (length == 2) {
      buf.writeUInt16BE(value);
    } else if (length == 4) {
      buf.writeUInt32BE(value);
    } else if (length == 8) {
      // console.log(value);
      // buf.writeBigUInt64BE(value);
    }
    return buf;
  },
  createIntBufferLE: (value, length) => {
    let buf = Buffer.alloc(length);
    if (length == 1) {
      buf.writeInt8(value);
    } else if (length == 2) {
      buf.writeInt16LE(value);
    } else if (length == 4) {
      buf.writeInt32LE(value);
    } else if (length == 8) {
      // console.log(value);
      // buf.writeBigUInt64BE(value);
    }
    return buf;
  },
  getCheckSum: function (buf) {
    let checksum = 0x00;
    for (let i = 0; i < buf.length; i++) {
      checksum = checksum + buf[i];
    }
    while (checksum > 0xFF) {
      let sum = checksum;
      checksum = 0x00;
      do {
        checksum += sum & 0xFF;
        sum = sum >>> 8;
      } while (sum > 0xFF);
    }
    let check = Buffer.alloc(1);
    check.writeUInt8(checksum)
    return check;
  },
  analyze: function (buf) {
    let offset = 0;
    offset += 2;
    if (buf.subarray(0, offset).toString('hex') !== 'faf5') {
      return false;
    }
    let length = buf.subarray(offset, ++offset),
      version = buf.subarray(offset, ++offset),
      serial = buf.subarray(offset, ++offset),
      command = buf.subarray(offset, ++offset);
    let dataEndOffset = offset + length[0] - 3;
    let data = buf.subarray(offset, dataEndOffset),
      check = buf.subarray(dataEndOffset, dataEndOffset + 1);
    let checkSum = this.getCheckSum(Buffer.concat([command, data], command.length + data.length));
    if (check.toString('hex') !== checkSum.toString('hex')) {
      return false;
    }
    return {
      version,
      serial,
      command,
      data
    }
  },
  date: require('./date'),
};
// 加载默认配置
config = core.getConfig('config');
// 加载日志配置
log4js.configure(core.getConfig('log4js'));
let loggerApp = log4js.getLogger('app'),
  server = null;

// events 异常捕捉
let onExit = function () {
  // close server
  if (server) {
    server.close();
  }
  setTimeout(() => process.exit(1), 500); // ensure all log has been written
};
let onError = (error, level) => {
  // log
  if (level === 'fatal') {
    loggerApp.fatal(error);
  } else {
    loggerApp.error(error);
  }
  onExit();
};
process.on('SIGTERM', onExit);
process.on('SIGINT', onExit);
process.on('uncaughtException', onError);

core.getErrorHandler = () => onError;
core.getLogger = (category) => log4js.getLogger(`[${process.pid}] ${category}`);
core.getService = (service) => server ? server.getService(service) : false;

// start method
core.start = _.once(() => {
  do {
    let argv = process.argv,
      op = 'start';
    if (argv.length == 3) {
      op = argv[2];
    }
    if (!_.includes(['start', 'stop', 'restart', 'status'], op)) {
      console.log('Invalid op.');
      break;
    }
    daemonize = config.server.daemonize || false;
    if (isMaster) {
      let pid = config.server.pid || path.join(pwdPath, '/main.pid'),
        removePid = false;
      if (op === 'stop' || op === 'restart') {
        // stop and restart
        if (!fs.existsSync(pid)) {
          console.log('Process not found.');
          break;
        } else {
          let processPid = parseInt(fs.readFileSync(pid, 'ascii'), 10);
          if (!isNaN(processPid)) {
            process.kill(processPid);
          }
        }
        if (op === 'stop') {
          break;
        } else {
          // wait pid file has been deleted
          while (fs.existsSync(pid)) {}
        }
      } else if (op === 'status') {
        // get status
        if (!fs.existsSync(pid)) {
          console.log('Process not found.');
          break;
        }
        // TODO get status
        break;
      }
      // start
      if (fs.existsSync(pid)) {
        console.log('Process has existed.');
        break;
      }
      if (daemonize) {
        // run in background
        if (process.env.__daemonize === undefined) {
          process.env.__daemonize = true;
          let child = require('child_process').spawn(argv.shift(), argv, {
            stdio: 'ignore',
            cwd: process.cwd(),
            env: process.env,
            detached: true
          });
          child.unref();
          break;
        }
      }
      fs.writeFileSync(pid, process.pid, 'ascii');
      removePid = true;
      process.on('exit', () => {
        if (removePid && fs.existsSync(pid)) {
          fs.unlinkSync(pid);
        }
      });
    }

    let domain = require('domain').create();
    domain.on('error', onError);
    domain.run(() => {
      if (isMaster) {
        let processes, childProcess;
        if (config.server.worker_processes !== undefined) {
          processes = Math.max(Math.min(parseInt(config.server.worker_processes, 10), 32), 1);
        } else {
          processes = require('os').cpus().length;
        }
        for (let i = 0; i < processes; ++i) {
          childProcess = cluster.fork({
            parent_pid: process.pid,
            child_index: i
          });
          workers[childProcess.process.pid] = childProcess;
        }
        loggerApp.info('Processes count: ' + processes);
      } else {
        server = require('./server')(core);
      }
    });
  } while (0);
});
module.exports = core;
